import is.glif.fenius.ws.draft.gen.DraftFeniusService;
import is.glif.fenius.ws.draft.gen.Draft;

import is.glif.fenius.ws.draft.gen.ReservationParams;
import is.glif.fenius.ws.draft.gen.Create;
import is.glif.fenius.ws.draft.gen.CreateReq;
import is.glif.fenius.ws.draft.gen.CreateResp;
import is.glif.fenius.ws.draft.gen.CreateResponse;
import is.glif.fenius.ws.draft.gen.ListReq;
import is.glif.fenius.ws.draft.gen.ListResponse;
import is.glif.fenius.ws.draft.gen.Cancel;
import is.glif.fenius.ws.draft.gen.CancelReq;
import is.glif.fenius.ws.draft.gen.CancelResp;
import is.glif.fenius.ws.draft.gen.CancelResponse;

import is.glif.fenius.ws.draft.gen.EndpointPair;
import is.glif.fenius.ws.draft.gen.ExplicitRoute;
import is.glif.fenius.ws.draft.gen.EndpointParams;
import is.glif.fenius.ws.draft.gen.CommonParams;
import is.glif.fenius.ws.draft.gen.DomainParams;
import is.glif.fenius.ws.draft.gen.Status;
import is.glif.fenius.ws.draft.gen.TimeParams;
import is.glif.fenius.ws.draft.gen.AvpSeq;
import is.glif.fenius.ws.draft.gen.Avp;

import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.namespace.QName;

import java.util.List;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.GregorianCalendar;

import java.net.URL;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.FiltersType;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;

public class FeniusDraftClient {
    static boolean debug = true;
    static String APOINT = "g-lambda.net:TOKYO";
    static String ZPOINT = "g-lambda.net:TSUKUBA";
    static String FENIUS_USER = "fenius-user";
    static String FENIUS_GROUP = "fenius-group";

    private DraftFeniusService tl = null;
    Draft service;
    DraftFeniusService port;
    FeniusClientConfig clientConfig = null;
    URL url = null;
    public String urlname = null;
    public QName qname = new QName("http://fenius.glif.is/ws/draft/", "draft");

    public FeniusDraftClient() 
    {
    }

    public FeniusDraftClient(FeniusClientConfig clientConfig)
    {
	this.clientConfig = clientConfig;
	urlname = clientConfig.getUrl();
	try {
	    url = new URL(urlname);
	} catch (Exception ex) {
	    System.err.println(ex);
	}
    }

    public synchronized void doCreate
	(String id, Calendar startTime, int duration,
	 int bandwidth, int vlan, String apoint, String zpoint) 
	throws Exception
    {
        if (clientConfig.isUseSSL()) {
            System.setProperty("javax.net.ssl.trustStoreType", "jks");  
            System.setProperty("javax.net.ssl.trustStore", clientConfig.getSslTruststorePath());  
            System.setProperty("javax.net.ssl.trustStorePassword", clientConfig.getSslTruststorePass());  
              
            System.setProperty("javax.net.ssl.keyStoreType", "jks");  
            System.setProperty("javax.net.ssl.keyStore", clientConfig.getSslKeystorePath());  
            System.setProperty("javax.net.ssl.keyStorePassword", clientConfig.getSslKeystorePass());  
        }

	if (port == null) {
	    if (url == null) service = new Draft();
	    else service = new Draft(url, qname);
	    port = service.getDraftFeniusServicePort();
	    if (clientConfig.isUseSSL()) {
		configureSSLOnTheClient(port, clientConfig);
	    }
	    if (clientConfig.isUseBasicHttpAuth()) {
		BindingProvider bp = (BindingProvider) port;
		bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, clientConfig.getHttpUsername());
		bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, clientConfig.getHttpPassword());
	    }
	}

	try {
	    ReservationParams resvParams = new ReservationParams();

	    GregorianCalendar startgc = 
		(GregorianCalendar) GregorianCalendar.getInstance();
	    Date startDate = startTime.getTime();
	    startgc.setTime(startDate);
	    GregorianCalendar endgc = 
		(GregorianCalendar) GregorianCalendar.getInstance();
	    Calendar endTime = (Calendar) startTime.clone();
	    endTime.add(Calendar.SECOND, duration);
	    Date endDate = endTime.getTime();
	    endgc.setTime(endDate);

	    DatatypeFactory df= DatatypeFactory.newInstance();
	    XMLGregorianCalendar startxgc = df.newXMLGregorianCalendar(startgc);
	    XMLGregorianCalendar endxgc = df.newXMLGregorianCalendar(endgc);

	    resvParams.setUserName(FENIUS_USER);
	    resvParams.setUserGroup(FENIUS_GROUP);
	    resvParams.setReservationId(id);

	    EndpointPair ep = new EndpointPair();
	    ep.setA(apoint);
	    ep.setZ(zpoint);
	    resvParams.setEndpoints(ep);

	    CommonParams cp = new CommonParams();
	    TimeParams tp = new TimeParams();
	    cp.setBandwidth(bandwidth);
	    cp.setStatus(Status.REQUESTING);
	    tp.setScheduledStartTime(startxgc);
	    tp.setScheduledEndTime(endxgc);
	    cp.setTimeParams(tp);

            AvpSeq avpseq = new AvpSeq();
            List<Avp> avps = avpseq.getAvp();
            Avp avp = new Avp();
            avp.setAttrName("eth.vlan");
            avp.setAttrVal("" + vlan);
            avps.add(avp);
            cp.setAdhocParams(avpseq);

	    resvParams.setEndtoendParams(cp);
	    
	    if (debug) printParams("Create request", resvParams);
	    Create wrapReq = new Create();
	    CreateReq createReq = new CreateReq();
	    wrapReq.setArg0(createReq);
	    createReq.setResvParams(resvParams);

            CreateResponse wrapResp = port.create(wrapReq);
            CreateResp createResp = wrapResp.getReturn();

	    ReservationParams rp = createResp.getResvParams();
	    if (debug) printParams("Create response", rp);
	} catch (Exception ex) {
	    ex.printStackTrace();
	    throw ex;
	}
    }

    public synchronized String doCancel(String id) 
	throws Exception
    {
        if (clientConfig.isUseSSL()) {
            System.setProperty("javax.net.ssl.trustStoreType", "jks");  
            System.setProperty("javax.net.ssl.trustStore", clientConfig.getSslTruststorePath());  
            System.setProperty("javax.net.ssl.trustStorePassword", clientConfig.getSslTruststorePass());  
              
            System.setProperty("javax.net.ssl.keyStoreType", "jks");  
            System.setProperty("javax.net.ssl.keyStore", clientConfig.getSslKeystorePath());  
            System.setProperty("javax.net.ssl.keyStorePassword", clientConfig.getSslKeystorePass());  
        }

	if (port == null) {
	    try {
		if (url == null) service = new Draft();
		else service = new Draft(url, qname);
		port = service.getDraftFeniusServicePort();
		if (clientConfig.isUseSSL()) {
		    configureSSLOnTheClient(port, clientConfig);
		}
		if (clientConfig.isUseBasicHttpAuth()) {
		    BindingProvider bp = (BindingProvider) port;
		    bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, clientConfig.getHttpUsername());
		    bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, clientConfig.getHttpPassword());
		}
	    } catch (Exception ex) {
		System.err.println("FeniusClinet:port is null in doCancel().");
		System.err.println(ex);
		throw ex;
	    }
	}

	try {
	    Cancel wrapReq = new Cancel();
	    CancelReq cancelReq = new CancelReq();
	    wrapReq.setArg0(cancelReq);
	    cancelReq.setUserName(FENIUS_USER);
	    cancelReq.setUserGroup(FENIUS_GROUP);
	    cancelReq.setReservationId(id);

	    CancelResponse wrapResp = port.cancel(wrapReq);
	    CancelResp cancelResp = wrapResp.getReturn();
	    Status s = cancelResp.getStatus();
	    
	    if (debug) {
		System.err.println("Cancel response return status=" + s);
	    }
	    return s.toString();
	} catch (Exception ex) {
	    ex.printStackTrace();
	    throw ex;
	}
    }

    public synchronized List<ReservationParams> doList() 
	throws Exception
    {
        if (clientConfig.isUseSSL()) {
            System.setProperty("javax.net.ssl.trustStoreType", "jks");  
            System.setProperty("javax.net.ssl.trustStore", clientConfig.getSslTruststorePath());  
            System.setProperty("javax.net.ssl.trustStorePassword", clientConfig.getSslTruststorePass());  
              
            System.setProperty("javax.net.ssl.keyStoreType", "jks");  
            System.setProperty("javax.net.ssl.keyStore", clientConfig.getSslKeystorePath());  
            System.setProperty("javax.net.ssl.keyStorePassword", clientConfig.getSslKeystorePass());  
        }

	if (port == null) {
	    if (url == null) service = new Draft();
	    else service = new Draft(url, qname);
	    port = service.getDraftFeniusServicePort();
	    if (clientConfig.isUseSSL()) {
		configureSSLOnTheClient(port, clientConfig);
	    }
	    if (clientConfig.isUseBasicHttpAuth()) {
		System.err.println("UseBasicHttpAuth.");
		BindingProvider bp = (BindingProvider) port;
		bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, clientConfig.getHttpUsername());
		bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, clientConfig.getHttpPassword());
	    }
	}

	try {
	    is.glif.fenius.ws.draft.gen.List wrapReq = new is.glif.fenius.ws.draft.gen.List();
	    ListReq listReq = new ListReq();
	    wrapReq.setArg0(listReq);
	    listReq.setUserName(FENIUS_USER);
	    listReq.setUserGroup(FENIUS_GROUP);

	    ListResponse result = port.list(wrapReq);
	    
	    if (result == null) return null;
	    List<ReservationParams> rps = result.getReturn().getReservations();

	    if (debug) {
		int size = rps.size();
		ReservationParams[] rp = new ReservationParams[size];
		ReservationParams[] rpp = rps.toArray(rp);
		for (int i = 0; i < size; i++) {
		    printParams("List response", rpp[i]);
		}
	    }
	    return rps;
	} catch (Exception ex) {
	    ex.printStackTrace();
	    throw ex;
	}
    }

    private void printParams(String m, ReservationParams p)
    {
	String s = m + ":\n";
	if (p != null) {
	    m += "\t" + p.getReservationId() 
		+ ":" + p.getEndtoendParams().getStatus().toString()
		+ " " + p.getEndpoints().getA()
		+ "->" + p.getEndpoints().getZ();

	    XMLGregorianCalendar c = 
		p.getEndtoendParams().getTimeParams().getScheduledStartTime();
	    m += ", From " + c.getYear() + "/" + c.getMonth() + "/" + c.getDay();
	    m += " " + c.getHour() + ":" + c.getMinute() + ":" + c.getSecond();

	    c = p.getEndtoendParams().getTimeParams().getScheduledEndTime();
	    m += ", To " /* + c.getYear() + "/" + c.getMonth() + "/" + c.getDay() */;
	    m += " " + c.getHour() + ":" + c.getMinute() + ":" + c.getSecond();

	    m += ", BW=" + p.getEndtoendParams().getBandwidth();

	    AvpSeq seq = p.getEndtoendParams().getAdhocParams();
	    String vlan = "0";
	    for (Avp avp : seq.getAvp()) {
		if (avp.getAttrName().equals("eth.vlan")) {
		    vlan = avp.getAttrVal();
		}
	    }
	    m += ", VLAN=" + vlan;
	}
	System.out.println(m);
    }

    private static void configureSSLOnTheClient(Object c, FeniusClientConfig clientConfig) { 
        org.apache.cxf.endpoint.Client client = ClientProxy.getClient(c); 
        HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
 
        TLSClientParameters tlsParams = new TLSClientParameters(); 
        tlsParams.setDisableCNCheck(true); 
        FiltersType filter = new FiltersType(); 
        filter.getInclude().add(".*_EXPORT_.*"); 
        filter.getInclude().add(".*_EXPORT1024_.*"); 
        filter.getInclude().add(".*_WITH_DES_.*"); 
        filter.getInclude().add(".*_WITH_NULL_.*"); 
        filter.getExclude().add(".*_DH_anon_.*"); 
        tlsParams.setCipherSuitesFilter(filter); 
        
        httpConduit.setTlsClientParameters(tlsParams); 
    } 

    public static void main(String[] args)
    {
	FeniusDraftClient fc = new FeniusDraftClient();

	Calendar startTime = Calendar.getInstance();
	startTime.setTime(new Date());
	startTime.set(Calendar.MILLISECOND, 0);
	startTime.add(Calendar.MINUTE, 1);

	try {
	    fc.doCreate("FeniusDraftClient", startTime, 120, 
			2000, 100, APOINT, ZPOINT);

	    int i = 15;
	    while (i-- > 0) {
		List<ReservationParams> l = fc.doList();
		Thread.sleep(10 * 1000);
	    }
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }
}